using System;
using System.Collections;
using Slaks.GraphExtensions;

namespace Slaks.SuperGraphExtensions.Containers
{
	public class SuperGraphList : CollectionBase
	{
		private SuperGraph m_centroid = null;
		private double m_distanceToCentroid = -100;

		private ArrayList m_temporalSuperGraphList = new ArrayList();

		public double GetAverageDistanceToCentroid()
		{
			return m_distanceToCentroid;
		}

		public SuperGraph GetCentroid()
		{
			return m_centroid;
		}

		public void SetCentroid(SuperGraph superGraph)
		{
			if (this.Contains(superGraph) == true) m_centroid = superGraph;
		}

		public void AddSuperGraphToTemporalList(SuperGraph superGraph)
		{
			m_temporalSuperGraphList.Add(superGraph);
		}

		public void MergeTemporalList()
		{
			SuperGraph superGraph;
			while (m_temporalSuperGraphList.Count > 0)
			{
				superGraph = (SuperGraph) m_temporalSuperGraphList[0];
				this.Add(superGraph);
				m_temporalSuperGraphList.RemoveAt(0);
			}
		}

		/// <summary>
		/// Determine centroid for the cluster of graphs 
		/// </summary>
		/// <param name="graphDistanceMeasure"></param>
		/// <returns>if the centroid changed return true otherwise return false</returns>
		public bool DetermineCentroid(SuperGraphDistanceMeasure graphDistanceMeasure)
		{
			if (this.Count == 0) return false;

			SuperGraph oldCentroid = m_centroid;
			SuperGraph candidateCentroid = null;

			int size = this.Count;

			double minDistance = double.MaxValue;
			double distance = 0.0;
			double[,] distanceMap = new double[size,size];

			int i = 0;
			int j = 0;

			for (i = 0; i < size; i++)
				for (j = 0; j < size; j++)
				{
					distanceMap[i, j] = distanceMap[j, i] = -100;
				}


			i = j = 0;

			double tempDistance;

			for (i = 0; i < size; i++)
			{
				SuperGraph graph1 = this[i];
				distance = 0.0;

				for (j = 0; j < size; j++)
				{
					SuperGraph graph2 = this[j];
					if (graph1 == graph2)
					{
						continue;
					}
					if (distanceMap[i, j] >= 0) distance += distanceMap[i, j];
					else if (distanceMap[j, i] >= 0) distance += distanceMap[i, j];
					else
					{
						tempDistance = graphDistanceMeasure(graph1, graph2);
						distance += tempDistance;
						distanceMap[j, i] = distanceMap[i, j] = tempDistance;
					}
				}

				distance /= size;

				if (minDistance > distance + 0.0000001)
				{
					minDistance = distance;
					candidateCentroid = graph1;
				}
			}

			m_distanceToCentroid = minDistance;
			m_centroid = candidateCentroid;

			if (oldCentroid == m_centroid) return false;
			return true;
		}

		#region CollectionBase

		public SuperGraph this[int index]
		{
			get { return ((SuperGraph) List[index]); }
			set { List[index] = value; }
		}

		public int Add(SuperGraph value)
		{
			return (List.Add(value));
		}

		public int IndexOf(SuperGraph value)
		{
			return (List.IndexOf(value));
		}

		public void Insert(int index, SuperGraph value)
		{
			List.Insert(index, value);
		}

		public void Remove(SuperGraph value)
		{
			List.Remove(value);
		}

		public bool Contains(SuperGraph value)
		{
			// If value is not of type SuperGraph, this will return false.
			return (List.Contains(value));
		}

		protected override void OnInsert(int index, Object value)
		{
			// Insert additional code to be run only when inserting values.
		}

		protected override void OnRemove(int index, Object value)
		{
			// Insert additional code to be run only when removing values.
		}

		protected override void OnSet(int index, Object oldValue, Object newValue)
		{
			// Insert additional code to be run only when setting values.
		}

		protected override void OnValidate(Object value)
		{
			// This check is commented out since it doesn't work for all types
			//if ( value.GetType() != Type.GetType("SuperGraph") )
			//	throw new ArgumentException( "value must be of type SuperGraph.", "value" );
		}

		#endregion
	}
}